﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Netfraction.Network.Protocol
{
    [global::System.AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    sealed class ProtocolFeatureAttribute : Attribute
    {
        public ProtocolFeatureAttribute()
        {
        }

        public string Name { get; set; }
    }

    public abstract class ProtocolFeature
    {
        private Dictionary<ProtocolFeatureAction, ProtocolMessageActionHandler> _protocolMessageActionHandlers;
        private List<ProtocolFeatureAction> _supportedActions;
        
        protected ProtocolFeature()
        {
            _protocolMessageActionHandlers = new Dictionary<ProtocolFeatureAction, ProtocolMessageActionHandler>();
            _supportedActions = new List<ProtocolFeatureAction>();
        }

        public static string GetFeatureName(Type type)
        {
            if (type.BaseType == typeof(Protocol.ProtocolFeature) && !type.IsAbstract)
            {
                object[] attribs = type.GetCustomAttributes(typeof(Protocol.ProtocolFeatureAttribute), false);
                if (attribs != null && attribs.Length > 0)
                    return (attribs[0] as Protocol.ProtocolFeatureAttribute).Name;
            }
            return string.Empty;
        }

        public static string GetFeatureName(ProtocolFeature feature)
        {
            return GetFeatureName(feature.GetType());
        }

        /// <summary>
        /// Get a dictionary of ProtocolMessageHandlers
        /// </summary>
        protected Dictionary<ProtocolFeatureAction, ProtocolMessageActionHandler> ProtocolMessageHandlers
        {
            get { return _protocolMessageActionHandlers; }
        }

        public virtual void HandleFeatureMessage(RemoteMachine.Node node, ProtocolMessage message, 
                                                IEnumerable<ProtocolFeature> extensionFeatures)
        {
            var a = from p in this.ProtocolMessageHandlers
                    where p.Key.ActionCode == message.MessageCode
                    select p;
            if (a.Count() > 0)
            {
                bool handlerResult;

                ProtocolMessageActionHandler handler = a.First().Value;

                handlerResult = handler.HandleProtocolMessage(node, message, extensionFeatures);
            }
            /*ProtocolFeatureAction pfa = new ProtocolFeatureAction(message.MessageCode, FeatureActionType.Any);
            if (this.ProtocolMessageHandlers.ContainsKey(pfa))
            {
                bool handlerResult;

                if (this.ProtocolMessageHandlers[pfa] != null)
                {
                    ProtocolMessageActionHandler handler = this.ProtocolMessageHandlers[pfa];

                    handlerResult = handler.HandleProtocolMessage(node, message, extensionFeatures);
                }
            }*/
        }

        public virtual object HandleExtensionMessage(RemoteMachine.Node node, ProtocolMessage message, int extensionType, params object[] args)
        {
            object returnValue = null;
            var a = from p in this.ProtocolMessageHandlers
                    where (GetAttribute<ProtocolMessageActionHandler, MessageExtensionAttribute>(p.Value, new MessageExtensionAttribute())).MessageName == message.MessageCode
                    && (GetAttribute<ProtocolMessageActionHandler, MessageExtensionAttribute>(p.Value, new MessageExtensionAttribute())).ExtensionType == extensionType
                    select p;
            if (a.Count() > 0)
            {
                ProtocolMessageActionHandler handler = a.First().Value;
                returnValue = handler.HandleMessageExtension(node, message, args);
            }
            return returnValue;
        }

        protected static U GetAttribute<T, U>(T t, U u) where U : System.Attribute
        {
            return t.GetType().GetCustomAttributes(u.GetType(), false)[0] as U;
        }

        /// <summary>
        /// Called when a Node connects
        /// </summary>     
        /// <returns>True if the ProtocolFeature handled the event; otherwise false</returns>
        public virtual bool HandleConnect(RemoteMachine.Node node)
        {
            return false;
        }

        /// <summary>
        /// Called when a Node disconnects
        /// </summary>        
        /// <returns>True if the ProtocolFeature handled the event; otherwise false</returns>
        public virtual bool HandleDisconnect(RemoteMachine.Node node)
        {
            return true;
        }

        public List<ProtocolFeatureAction> SupportedActions
        {
            get
            {
                return _supportedActions;
            }
        }

        /// <summary>
        /// Obtains the Actions a Feature supports via the const string fields in a class/struct passed to it.
        /// </summary>
        /// <parameter name="featureActions">A class or struct containing a set of const string fields.</parameter>
        /// <returns>A list of the Action protocol codes.</returns>
        protected List<ProtocolFeatureAction> GetFeatureActions(object featureActions)
        {
            List<ProtocolFeatureAction> actions = new List<ProtocolFeatureAction>();

            Type t = featureActions.GetType();
            System.Reflection.FieldInfo[] fields = t.GetFields();
            foreach (System.Reflection.FieldInfo field in fields)
            {
                actions.Add(new ProtocolFeatureAction(field.GetValue(null) as string,
                    (field.GetCustomAttributes(typeof(ProtocolFeatureActionAttribute), false)[0] as ProtocolFeatureActionAttribute).ActionType));
            }

            return actions;
        }

        public bool SupportsAction(string messageCode)
        {
            var res = from p in _supportedActions
                      where p.ActionCode == messageCode
                      select p;
            return res.Count() > 0;
        }

        public bool SupportsActionOfType(string messageCode, FeatureActionType actionType)
        {
            var res = from p in _supportedActions
                      where p.ActionCode == messageCode && p.ActionType == actionType
                      select p;
            return res.Count() > 0;
        }
    }

}
